package com.twu.biblioteca;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import org.apache.commons.lang3.StringUtils;


interface BibliotecaAppCommand {
    String runCommand(int userInput);
}


public class BibliotecaApp {
    private static Biblioteca biblioteca;
    private static Map submenuHeaderMap = new HashMap();
    private static Map submenuPromptMap = new HashMap();
    private static Map submenuInvalidAnswerMap = new HashMap();
    private static Map<String, BibliotecaAppCommand> submenuMethodMap = new HashMap<String, BibliotecaAppCommand>();

//APP STARTUP METHODS
    public static void main(String[] args) throws Exception{
        biblioteca = new Biblioteca();
        startBiblioteca();
        mainMenu();
    }

    private static void startBiblioteca(){
        assignSubmenuMaps();
        fillAvailableMainMethodsMap();
        printWelcomeMessage();
    }

    private static void exitBiblioteca(){
        printStringBetweenLines("THANKS FOR USING THE BIBLIOTECA APP - SEE YOU SOON!");
        System.exit(0);
    }

//APP CONTROLLER METHODS
    private static void mainMenu() throws Exception{
        String[] castedArraySubmenu;
        String[] castedArrayData;
        String chosenSubmenu;

        printMenu("MAIN MENU (CHOOSE BY ENTERING [NUMBER] IN BRACKET)", biblioteca.listMainMenu(), true);
        String userInput = getUserInput("Your selection: ");
        handleMainManuUserInput(userInput);

        castedArraySubmenu = (String[]) castModelAPIArray(userInput).get("Submenu");
        chosenSubmenu = castedArraySubmenu[0];
        castedArrayData = (String[]) castModelAPIArray(userInput).get("Data");
        submenuResponseHandler(chosenSubmenu, castedArrayData);
    }

    private static void handleSubMenuUserInput(String submenuName, String[] arrayToBeDisplayed) throws Exception{
        printSubmenuToScreen((String) submenuHeaderMap.get(submenuName), arrayToBeDisplayed);
        String userInput = getUserInput((String) submenuPromptMap.get(submenuName));

        if (userInput.toUpperCase().equals("B")) {
            mainMenu();
        } else if (!submenuUserInputHandler(userInput)) {
            recursivelyCallMethod(submenuName, arrayToBeDisplayed, "Invalid input!");
        } else {
            matchAPIAnswerValidity(submenuName, userInput, arrayToBeDisplayed);
        }
    }

//HELPER METHODS
//APP STARTUP
    private static void assignSubmenuMaps() {
        submenuHeaderMap = biblioteca.getSubmenuMapInfo("Header");
        submenuPromptMap = biblioteca.getSubmenuMapInfo("Prompt");
        submenuInvalidAnswerMap = biblioteca.getSubmenuMapInfo("Invalid");
    }

    private static void fillAvailableMainMethodsMap(){
        submenuMethodMap.put("List Books", new BibliotecaAppCommand() {
            public String runCommand(int userInput) { return biblioteca.checkoutBook(userInput); };
        });
        submenuMethodMap.put("Return Book", new BibliotecaAppCommand() {
            public String runCommand(int userInput) { return biblioteca.returnBook(userInput); };
        });
    }

//MODEL API HANDLING
    private static Map castModelAPIArray(String userInput) {
        int parsedUserInput = Integer.parseInt(userInput);

        return (Map) biblioteca.chooseMenuOption(parsedUserInput);
    }

    private static void submenuResponseHandler(String chosenSubmenu, String[] castedArrayData) throws Exception{
        if (chosenSubmenuInvalid(chosenSubmenu)) {
            mainMenu();
        } else {
            handleSubMenuUserInput(chosenSubmenu, castedArrayData);
        }
    }

    private static void matchAPIAnswerValidity (String submenuName, String userInput, String[] arrayToBeDisplayed) throws Exception{
        String answer = submenuMethodMap.get(submenuName).runCommand(Integer.parseInt(userInput));
        if(answer.equals(submenuInvalidAnswerMap.get(submenuName))){
            recursivelyCallMethod(submenuName, arrayToBeDisplayed, answer);
        } else {
            mainMenuCall(answer);
        }
    }

//USER OUTPUT AND INPUT HANDLING
    private static String getUserInput(String prompt) {
        Scanner scanner = new Scanner (System.in);

        if(prompt.length() > 0) {
            System.out.print(prompt);
        }

        return scanner.next();
    }

    private static void handleMainManuUserInput(String userInput)  throws Exception{
        if (userInput.toUpperCase().equals("Q")) {
            exitBiblioteca();
        } else if (!submenuUserInputHandler(userInput)) {
            mainMenuCall("Invalid input!");
        }
    }

    private static Boolean submenuUserInputHandler(String userInput) {
        return StringUtils.isNumeric(userInput);
    }

    private static Boolean chosenSubmenuInvalid(String chosenSubmenu){
        return chosenSubmenu.equals("Invalid");
    }

//GENERAL HELPER METHODS
    private static void mainMenuCall(String prompt) throws Exception{
        printStringBetweenLines(prompt);
        mainMenu();
    }

    private static void recursivelyCallMethod(String submenuName, String[] listInformation, String prompt) throws Exception{
        printDividerLine();
        printStringToScreen(prompt);
        handleSubMenuUserInput(submenuName, listInformation);
    }

//SCREEN OUTPUT
    private static void printWelcomeMessage(){
        printStringBetweenLines(biblioteca.welcome());
    }

    private static void printMenu(String menuTitle, String[] listArrayItems, Boolean isMainMenu) {
        if(isMainMenu) printDividerLine();
        printStringToScreen(menuTitle);
        printDividerLine();
        printArrayToScreen(listArrayItems);
        if(isMainMenu) printLogoutChoice();
        if(!isMainMenu) printMainMenuReturnChoice();
        printDividerLine();
    }

    private static void printSubmenuToScreen(String title, String[] listInformation) {
        printStringBetweenLines(title);
        printArrayToScreen(listInformation);
        printMainMenuReturnChoice();
        printDividerLine();
    }

    private static void printStringBetweenLines(String stringToBePrinted){
        printDividerLine();
        printStringToScreen(stringToBePrinted);
        printDividerLine();
    }

    private static void printStringToScreen(String stringToBePrinted) {
        System.out.println(stringToBePrinted);
    }

    private static void printDividerLine(){
        printStringToScreen("--------------------------------------------------");
    }

    private static void printLogoutChoice() {
        printStringToScreen("\n[Q] Logout");
    }

    private static void printMainMenuReturnChoice() {
        printStringToScreen("\n[B] Back to Main Menu");
    }

    private static void printArrayToScreen(String[] arrayToBePrinted) {
        for(int i=0; i<arrayToBePrinted.length; i++){
            int bookNumber = i+1;
            printStringToScreen("["+bookNumber+"] "+arrayToBePrinted[i]);
        }
    }
}
